#include "os_eflags.h"
#include "os_mem.h"
#include "os_list.h"
#include "os_task.h"
#include "os_sched.h"

#if OS_EFLAGS_ENABLE == 1

#if !OS_EFLAGS_DBG_PRINT_ENABLE
#undef os_dbg
#define os_dbg(fmt, ...) do {}while (0)
#endif

/**
 * 任务等待结构
*/
typedef struct _eflags_wait_t {
    int type;                       // 等待的类型
    os_flags_t flags;               // 等待的标志位
    os_flags_t result;              // 等待结果，哪些位置1或清0
}eflags_wait_t;

/**
 * 初始化事件标志
 * @param eflags 事件标志
 * @param init_flags 初始的标志位
*/
os_err_t eflags_init (os_eflags_t * eflags, os_flags_t init_flags, int flags) {
    // 初始化等待结构
    os_err_t err = os_event_init(&eflags->event, OS_EVENT_TYPE_EFLAGS, flags);
    if (err < 0) {
        os_dbg("create eflags event failed: %s.", "323");
        return err;
    }

    // 设置初始标志
    eflags->flags = init_flags;
    return OS_ERR_OK;
}

/**
 * 初始化事件标志
 * @param eflags 事件标志
 * @param init_flags 初始的标志位
*/
os_err_t os_eflags_init (os_eflags_t * eflags, os_flags_t init_flags) {
    os_param_failed(eflags == OS_NULL, OS_ERR_PARAM);

    return eflags_init(eflags, init_flags, 0);
}

/**
 * 取消事件标志的初始化
 * @param eflags 事件标志
*/
os_err_t os_eflags_uninit (os_eflags_t * eflags) {       
    os_param_failed(eflags == OS_NULL, OS_ERR_PARAM);

    // 唤醒所有任务后，尝试进行调试
    os_int_status_t status = os_sched_int_lock();
    int cnt = os_event_wait_cnt(&eflags->event);
    os_event_uninit(&eflags->event);
    os_sched_int_restore(status);

    // 有任务唤醒时，进行调度，以便运行更高优先级的任务
    if (cnt > 0) {
        os_sched_run();
    } 

    return OS_ERR_OK;
}

#if OS_MEM_ENABLE
/**
 * 分配一个事件标志
 * @param init_cnt 事件标志初始值
 * @param max_cnt 事件标志最大计数值, 如果为0时表示没有限制
 * @return 创建的事件标志
 */
os_eflags_t * os_eflags_create (os_flags_t init_flags) {
    // 分配内存
    os_eflags_t * eflags = os_mem_malloc(sizeof(os_eflags_t));
    if (eflags == OS_NULL) {
        os_dbg("error: alloc eflags failed");
        return OS_NULL;
    }

    // 初始化事件标志结构
    os_err_t err = eflags_init(eflags, init_flags, OS_OBJECT_HEAP);
    if (err < 0) {
        os_dbg("error: init eflags failed.");
        os_mem_free(eflags);
        return OS_NULL;
    }

    return eflags;
}

/**
 * 释放一个事件标志
 * @param 需要释放的事件标志
 */
os_err_t os_eflags_free (os_eflags_t * eflags) {
    os_param_failed(eflags == OS_NULL, OS_ERR_PARAM);

    os_eflags_uninit(eflags);
    if (eflags->event.flags & OS_OBJECT_HEAP) {
        os_mem_free(eflags);
    }

    return OS_ERR_OK;
}
#endif

/**
 *  等待事件标志组中特定的标志
 * @param eflags 等待的事件标志组
 * @param ms 等待的时间，毫秒为单位
 * @param type 等待的方式, OS_EFLAGS_CLEAR/SET | OS_EFLAGS_ANY/ALL | OS_EFLAGS_EXIT_CLEAR
 * @param flags 等待的标志位
 * @param result 当满足条件时的相应结果
*/
os_flags_t os_eflags_wait (os_eflags_t * eflags, int ms, int type, os_flags_t flags,  os_err_t * p_err) {
    os_param_failed_exec(eflags == OS_NULL, 0, if (p_err) *p_err = OS_ERR_PARAM);
    os_param_failed(flags == 0, OS_ERR_PARAM);

    os_int_status_t status = os_sched_int_lock();

    // 先检查已有的条件是否满足
    int check_all = type & OS_EFLAGS_ALL;
    if (type & OS_EFLAGS_SET) {
        // 置位检查
        os_flags_t set_flags = eflags->flags & flags;      // 取出哪些位置1了
        if ((check_all && (flags == set_flags)) || (!check_all && (set_flags != 0))) {
            // 检查所有位，且所有的要检查位的位都置1。或者检查任意位，且任意位为1
            if (type & OS_EFLAGS_EXIT_CLEAR) {
                // Set，退出时清0
                eflags->flags &= ~set_flags; 
            }

            os_sched_int_restore(status);

            if (p_err) {
                *p_err = OS_ERR_OK;
            }            
            return set_flags;
        } 
    } else {
        // 清0位检查
        os_flags_t clear_flags = ~eflags->flags & flags;     // 取出哪些位已经清零了
        if ((check_all && (flags == clear_flags)) || (!check_all && (clear_flags != 0))) {
            if (type & OS_EFLAGS_EXIT_CLEAR) {
                // clear, 置1所有的位
                eflags->flags |= flags;     // 将这些位置1
            }

            os_sched_int_restore(status);

            if (p_err) {
                *p_err = OS_ERR_OK;
            }  
            return clear_flags;
        }
    }

    // 条件不满足，当前任务需要等待
    eflags_wait_t wait;
    wait.type = type;
    wait.flags = flags;
    
    os_event_wait(&eflags->event, &wait, ms);
    os_sched_run();
    os_sched_int_restore(status);

    // 取结果
    os_task_t * self = os_task_self();
    if (p_err) {
        *p_err = self->event_wait.err;
    }
    return self->event_wait.err == OS_ERR_OK ? wait.result : 0;
}

/**
 * 通知事件标志组中的任务有新的标志发生
 * @param eflags 事件标志
 * @param type 释放的事件类型
 * @param flags 释放的标志位
 */
os_err_t os_eflags_release (os_eflags_t * eflags, int type, os_flags_t flags) {
    os_int_status_t status = os_sched_int_lock();

    // 先清除或置位其听 标志位
    if (type & OS_EFLAGS_SET) {
        eflags->flags |= flags;
    } else {
        eflags->flags &= ~flags;
    }

    // 遍历所有的等待任务, 获取满足条件的任务，加入到待移除列表中
    int need_sched = 0;
    os_flags_t temp_flags = eflags->flags;
    
    // 这里只能遍历所有任务，然后找标志位匹配的, 然后还要唤醒该任务
    // 考虑到遍历过程中有对任务进行移除，会影响到后续遍历，所以这里采用了一种简单的方法
    int task_cnt = os_list_count(&eflags->event.wait_list);
    for (int i = 0; i < task_cnt; i++) {
        // 取首个任务
        os_task_t * first_task = (os_task_t *)os_list_first(&eflags->event.wait_list);
        os_assert(first_task != OS_NULL);

        // 判断是否满足条件
        eflags_wait_t * wait = first_task->event_wait.reason;
        int check_all = wait->type & OS_EFLAGS_ALL;
        if (wait->type & OS_EFLAGS_SET) {
            // 置位检查
            os_flags_t set_flags = eflags->flags & wait->flags;      // 取出哪些位置1了
            if ((check_all && (wait->flags == set_flags)) || (!check_all && (set_flags != 0))) {
                // 检查所有位，且所有的要检查位的位都置1。或者检查任意位，且任意位为1
                if (wait->type & OS_EFLAGS_EXIT_CLEAR) {
                    temp_flags &= ~set_flags; 
                }

                // 满足条件，唤醒任务
                os_event_wakeup_task(&eflags->event, first_task, OS_NULL, OS_ERR_OK);
                wait->result = set_flags;
                need_sched = 1;
                continue;
            } 
        } else {
            // 清0位检查
            os_flags_t clear_flags = ~eflags->flags & wait->flags;     // 取出哪些位已经清零了
            if ((check_all && (wait->flags == clear_flags)) || (!check_all && (clear_flags != 0))) {
                if (wait->type & OS_EFLAGS_EXIT_CLEAR) {
                    temp_flags |= flags;     // 将这些位置1
                }

                // 满足条件，唤醒任务
                os_event_wakeup_task(&eflags->event, first_task, OS_NULL, OS_ERR_OK);
                need_sched = 1;
                wait->result = clear_flags;
                continue;
            }
        }

        // 不满足条件，加入到队列尾部
        if (task_cnt > 1) {
            os_event_list_remove(&eflags->event, first_task);
            os_event_list_insert(&eflags->event, first_task);
        }
    }

    // 如果有任务就绪，则执行一次调度
    if (need_sched) {
        eflags->flags = temp_flags;
        
        os_sched_run();
    }

    os_sched_int_restore(status);
    return OS_ERR_OK;
}

/**
 * 获取事件标志当前计数
 * @param eflags 操作的事件标志
 * @return 当前的事件标志
 */
os_flags_t os_eflags_get_flags (os_eflags_t * eflags, os_err_t * err) {
    os_param_failed(eflags == OS_NULL, 0);

    os_int_status_t status = os_sched_int_lock();
    os_flags_t flags = eflags->flags;
    os_sched_int_restore(status);

    if (err) {
        *err = OS_ERR_OK;
    }
    return flags;
}

/**
 * 获取等待事件标志的任务数量
 * @param eflags 事件标志
 * @return 事件标志的最大计数
 */
os_cnt_t os_eflags_task_cnt (os_eflags_t * eflags) {
    os_param_failed(eflags == OS_NULL, -1);

    os_int_status_t status = os_sched_int_lock();
    os_cnt_t cnt = os_event_wait_cnt(&eflags->event);
    os_sched_int_restore(status);
    return cnt;
}

#endif // OS_EFLAGS_ENABLE
